﻿/*
 * The MIT License (MIT)
 * Copyright (c) 2012 @donkeysharp
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 * and associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software 
 * is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies 
 * or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
/* 
 *    : () { :|:& }; :
 *    Coded by @donkeysharp
 */
namespace DonkeyCleaner.Core {
    public class MalwareScanner {
        /// <summary>
        /// Gets or sets the starting root path for scanning
        /// </summary>
        public string RootPath { get; set; }

        /// <summary>
        /// Initializes a new instance for the DonkeyCleaner.Core.MalwareCleaner
        /// class using a root path to start scanning.
        /// </summary>
        /// <param name="rootPath">Root path to start scanning</param>
        /// <param name="comparisonMethod">Comparison method for hash scan</param>
        public MalwareScanner(string rootPath) {
            RootPath = rootPath;
        }
        /// <summary>
        /// Returns a list of possible malware files, this method just
        /// checks for .exe files with the same directory name
        /// </summary>
        /// <returns>List of possible malware filenames </returns>
        public List<Malware> DirectoryRecursiveMalwareScan() {
            // Gonna use BFS to visit all directories and its files
            Queue<string> directories = new Queue<string>();
            List<Malware> malwareList = new List<Malware>();
            
            DirectoryInfo directoryInfo;
            string currentDirectory;

            directories.Enqueue(RootPath);
            while(directories.Count != 0) {
                currentDirectory = directories.Dequeue();
                directoryInfo = new DirectoryInfo(currentDirectory);

                // Checks for .exe files with the same directory name
                string tmp = currentDirectory + "\\" + directoryInfo.Name + ".exe";
                if (File.Exists(tmp)) {
                    Malware malware = new Malware() { 
                        Name = tmp,
                        MD5Sum = "",
                        Size = new FileInfo(tmp).Length,
                        MalwareType = MalwareType.DirectoryClone
                    };
                    malwareList.Add(malware);
                    Logging.Logger.SaveLogMessage(Logging.LogType.ThreatDetected, "Same directory executable, possible malware( " + tmp + ")");
                }

                foreach (DirectoryInfo dir in directoryInfo.GetDirectories()) {
                    directories.Enqueue(dir.FullName);
                }
            }

            return malwareList;
        }
        /// <summary>
        /// Returns a list of possible malware files, this method just
        /// checks for .exe files that try to replace another filetype using
        /// its name but using a .exe extension
        /// </summary>
        /// <returns>List of possible malware filename</returns>
        public List<Malware> FileRecursiveMalwareScan() {
            Queue<string> directories = new Queue<string>();
            List<Malware> malwareList = new List<Malware>();

            DirectoryInfo directoryInfo;
            string currentDirectory;

            directories.Enqueue(RootPath);
            while(directories.Count != 0) {
                currentDirectory = directories.Dequeue();
                
                directoryInfo = new DirectoryInfo(currentDirectory);
                
                // Some files maybe cannot be accessed by this process
                // and can throw a security exception, but maybe it can
                // throw another exception
                try {
                    foreach (FileInfo file in directoryInfo.GetFiles("*.exe")) {
                        // Removes .exe extension and checks if that file exists
                        string possibleMalware = file.FullName.Substring(0, file.FullName.Length - 4);
                        
                        if (File.Exists(possibleMalware)) {
                            Malware malware = new Malware() { 
                                Name = file.FullName,
                                MD5Sum = "",
                                Size = new FileInfo(possibleMalware).Length,
                                MalwareType = MalwareType.FileClone
                            };

                            malwareList.Add(malware);
                            Logging.Logger.SaveLogMessage(Logging.LogType.ThreatDetected, "Executable trying to replace " + possibleMalware + ", possible malware( " + file.FullName + ")");
                        }
                    }
                }
                catch (System.Security.SecurityException sEx) {
                    // LOL
                    Logging.Logger.SaveLogMessage(Logging.LogType.PermissionError, sEx.Message);
                }
                catch (Exception ex) {
                    Logging.Logger.SaveLogMessage(Logging.LogType.Other, ex.Message);
                }

                foreach (DirectoryInfo dir in directoryInfo.GetDirectories()) {
                    directories.Enqueue(dir.FullName);
                }
            }

            return malwareList;
        }
        /// <summary>
        /// Returns a list of possible malware files, this method just
        /// checks database hashes and compares'em with scanned file hash
        /// </summary>
        /// <returns>List of possible malware filename</returns>
        public List<Malware> HashBasedRecursiveScan() {
            Queue<string> directories = new Queue<string>();
            List<Malware> malwareList = new List<Malware>();

            string currentDirectory;
            DirectoryInfo directoryInfo;

            directories.Enqueue(RootPath);
            while (directories.Count != 0) {
                currentDirectory = directories.Dequeue();
                directoryInfo = new DirectoryInfo(currentDirectory);

                // Get all executable files
                foreach (FileInfo file in directoryInfo.GetFiles("*.exe")) {
                    string hashSum = HashUtil.ComputeFileMD5Sum(file.FullName);
                    if (MalwareDatabase.Current.CheckHash(hashSum)) {
                        Malware malware = new Malware() { 
                            Name = file.FullName,
                            MD5Sum = hashSum,
                            Size = new FileInfo(file.FullName).Length,
                            MalwareType = MalwareType.DatabaseMalware
                        };
                        malwareList.Add(malware);
                        Logging.Logger.SaveLogMessage(Logging.LogType.ThreatDetected, "Database detected malware( " + file.FullName + ")");
                    }
                }

                foreach (DirectoryInfo dir in directoryInfo.GetDirectories()) {
                    directories.Enqueue(dir.FullName);
                }
            }

            return malwareList;
        }
    }
}
